<?php

class HABTMAssociationDAO extends AssociationDAO
{

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/domain/association/AssociationDAO#afterSave($client)
   *
   * TODO: associated data should only be deleted if association is exclusive
   * TODO: check if associations are unique
   * TODO: check how to add extra data to association table, such as order information
   */
  public function afterSave(&$client)
  {
    $name = $this->getDefinition()->getName();

    if (isset($client[$name])) {
      $pk = $this->getDefinition()->getPrimaryKeyField()->getName();

      if (! isset($client[$pk])) {
        throw new Exception('Cannot save associated data: primary key of source object not set');
      }

      $fk = $this->getDefinition()->getForeignKeyField()->getName();

      $_client = array(
        $pk => $client[$pk]
      );

      $this->find($_client);

      $diff = sy_data_diff($_client[$name], $client[$name], $this->getDefinition()->getTarget()->getPkName());

      $dao = RepositoryDAO::getInstance($this->getDefinition()->getTarget()->getName());

      /*foreach ($diff['delete'] as $row) {
        $dao->delete($row);
      }*/

      $client[$name] = array_merge($diff['create'], $diff['update']);

      foreach ($client[$name] as &$data) {
        $data[$fk] = $client[$pk];

        $dao->save($data);
      }

      $this->saveHABTM($client);
    }
  }

  protected function saveHABTM($client)
  {
    $name = $this->getDefinition()->getName();

    $atable = $this->getDefinition()->getAssociationTable();
    $afk = $this->getDefinition()->getAssociationForeignKey();
    $apk = $this->getDefinition()->getAssociationPrimaryKey();

    $table = $this->getDefinition()->getTarget()->getTable();
    $fk = $this->getDefinition()->getForeignKeyField()->getName();
    $pk = $this->getDefinition()->getPrimaryKeyField()->getName();

    $id = $client[$pk];

    $dao = DAO::getInstance();

    $dao->query("DELETE FROM $atable WHERE $apk = ?")->execute($id);

    foreach ($client[$name] as $row) {
      $_data = array(
        $apk => $id, $afk => $row[$fk]
      );

      $sql = "INSERT INTO $atable " . QueryObject::buildInsert($_data, '?');

      $query = $dao->query($sql);

      $result = $query->execute(array_values($_data));
    }
  }

  /**
   * (non-PHPdoc)
   * @see simplify/kernel/domain/association/AssociationDAO#find($clients)
   */
  public function find(&$clients, $params = null)
  {
    $dao = DAO::getInstance();

    $name = $this->getDefinition()->getName();

    $atable = $this->getDefinition()->getAssociationTable();
    $afk = $this->getDefinition()->getAssociationForeignKey();
    $apk = $this->getDefinition()->getAssociationPrimaryKey();

    $table = $this->getDefinition()->getTarget()->getTable();
    $fk = $this->getDefinition()->getForeignKeyField()->getName();
    $pk = $this->getDefinition()->getPrimaryKeyField()->getName();

    $fields = AbstractRepository::collectFields($this->getDefinition()->getTarget());

    $query = $dao->query()->from($table)->join($atable, "$atable.$afk = $table.$fk", QueryObject::JOIN_LEFT);

    $params = ( array ) $params;
    if (isset($params['where'])) {
      $query->where($params['where']);
    }

    if (isset($clients[$pk])) {
      $id = $clients[$pk];

      $query->where("$apk = ?");

      $result = $query->select($fields)->execute($id);

      $data = $result->fetchAll();

      $clients[$name] = $data;
    }
    else {
      $ids = array();
      foreach ($clients as $client) {
        $ids[] = $client[$pk];
      }

      if (! empty($ids)) {
        $fields[] = "$atable.$apk AS $apk";

        $ids = array_unique($ids);
        $ids = implode(', ', $ids);

        $query->select($fields)->where("$apk IN ($ids)");

        $result = $query->execute();

        $data = $result->fetchAll();

        $_data = array();
        foreach ($data as $row) {
          if (! isset($_data[$row[$apk]])) {
            $_data[$row[$apk]] = array();
          }

          $id = $row[$apk];
          unset($row[$apk]);

          $_data[$id][] = $row;
        }
        $data = $_data;

        foreach ($clients as & $client) {
          if (isset($data[$client[$pk]])) {
            $client[$name] = $data[$client[$pk]];
          }
          elseif ($this->getDefinition()->isRequired()) {
            $client[$name] = array();
          }
        }

        if (isset($data[''])) {
          foreach ($clients as & $client) {
            if (! isset($client[$name])) {
              $client[$name] = array();
            }
            
            $client[$name] = array_merge($client[$name], $data['']);
          }
        }
      }
    }
  }

}

?>